import asyncio

from aiogram import F, Router, types, Bot
from aiogram.fsm.context import FSMContext
from loguru import logger

from database import models
from tg_bot.filters.user_filter import IsAdmin
from tg_bot.keyboards import inline, callback_factory as cf
from tg_bot.services.broadcaster import send_message
from tg_bot.services.text import not_correct_input
from tg_bot.states.admin import AdminCmd, AdminWorkSeat

router = Router()
router.message.filter(IsAdmin())
router.callback_query.filter(IsAdmin())


@router.callback_query(cf.AdminMenuCall.filter(
    F.item == 'work_seat'
), AdminCmd.main)
@router.callback_query(cf.BackCall.filter(
    F.to == 'work_seat'
), AdminWorkSeat())
async def work_seat_call(call: types.CallbackQuery,
                         state: FSMContext):
    await work_seat_message(call.message, state)


async def work_seat_message(message: types.Message,
                            state: FSMContext):
    data = await state.get_data()
    team: models.Team = data.get('team')
    if not team:
        text = 'Выберите, кто делает заказы: '
        key = await inline.team_choice(back_to='admin_menu')
        await state.set_state(AdminWorkSeat.team_choice)
    else:
        text = 'Работа со столами: '
        key = await inline.free_and_busy_seat(back_to='admin_menu')
        await state.set_state(AdminWorkSeat.main)
    try:
        await message.edit_text(text, reply_markup=key)
    except Exception as e:
        logger.warning(e)
        await message.answer(text, reply_markup=key)


@router.callback_query(cf.TeamCall.filter(), AdminWorkSeat.team_choice)
async def team_choice_call(call: types.CallbackQuery,
                           state: FSMContext,
                           callback_data: cf.TeamCall):
    team: models.Team = models.Team.objects.filter(pk=callback_data.pk).first()
    await state.update_data({'team': team})
    await work_seat_call(call, state)


@router.callback_query(cf.SeatCall.filter(), AdminWorkSeat.main)
async def seat_choice_call(call: types.CallbackQuery,
                           state: FSMContext,
                           callback_data: cf.SeatCall):
    seat: models.Seat = models.Seat.objects.get(pk=callback_data.pk)
    await seat_choice_message(call.message, state, seat)


@router.callback_query(cf.BackCall.filter(
    F.to == 'seat_choice'
), AdminWorkSeat())
@router.callback_query(cf.BackCall.filter(
    F.to == 'seat_info'
), AdminWorkSeat.SeatActions())
async def back_to_seat_choice_call(call: types.CallbackQuery,
                                   state: FSMContext):
    data = await state.get_data()
    seat: models.Seat = data.get('seat')
    await seat_choice_message(call.message, state, seat)


async def seat_choice_message(message: types.Message,
                              state: FSMContext,
                              seat: models.Seat):
    data = await state.get_data()
    team: models.Team = data.get('team')
    await state.set_data({'seat': seat, 'team': team})

    if seat.status:
        text = seat.get_name()
        key = await inline.concatenate_key(
            await inline.open_seat(),
            await inline.back('work_seat')
        )
    else:
        page: int = data.get('page', 1)
        sales_main: models.SalesMain = seat.get_active_sales_main()
        text = sales_main.get_text()
        key = await inline.busy_seat_menu(sales_main, page=page, back_to='work_seat')
        await state.update_data({'sales_main': sales_main,
                                 'page': page})
    try:
        await message.edit_text(text, reply_markup=key)
    except Exception as e:
        logger.warning(e)
        await message.answer(text, reply_markup=key)
    await state.set_state(AdminWorkSeat.SeatActions.main)


@router.callback_query(cf.AdminActionCall.filter(
    F.action == 'open_seat'
), AdminWorkSeat.SeatActions.main)
async def open_seat_call(call: types.CallbackQuery,
                         state: FSMContext):
    data = await state.get_data()
    seat: models.Seat = data.get('seat')
    seat.open_seat()
    await seat_choice_message(call.message, state, seat)


@router.callback_query(cf.AdminActionCall.filter(
    F.action == 'close_seat'
), AdminWorkSeat.SeatActions.main)
async def close_seat_call(call: types.CallbackQuery, state: FSMContext,
                          bot: Bot):
    data = await state.get_data()
    seat: models.Seat = data.get('seat')
    sales_main: models.SalesMain = data.get('sales_main')
    broadcaster_data = sales_main.add_cashback()
    for user, cashback_sum in broadcaster_data:
        if cashback_sum <= 0:
            continue
        await send_message(bot, user.user_id, f'Вам начислено {cashback_sum} бонусов!\n'
                                              f'Остаток бонусов: <b>{user.get_balance()}</b>')
    seat.close_seat()
    await work_seat_call(call, state)


#   ADD SALE
@router.callback_query(cf.AdminActionCall.filter(
    F.action == 'add_sale'
), AdminWorkSeat.SeatActions.main)
@router.callback_query(cf.BackCall.filter(
    F.to == 'add_sale'
), AdminWorkSeat.AddSale.product_choice)
async def add_sale_call(call: types.CallbackQuery, state: FSMContext):
    data = await state.get_data()
    text = 'Выберите нужную категорию: '
    current_page = data.get('CategoryPaginator_page', 1)
    key = inline.CategoryPaginator(
        router,
        current_page=current_page,
        after_pagination_keyboards=[await inline.back('seat_choice')]
    )
    try:
        await call.message.edit_text(text, reply_markup=key.as_markup())
    except Exception as e:
        logger.warning(e)
        await call.message.answer(text, reply_markup=key.as_markup())
    await state.set_state(AdminWorkSeat.AddSale.category_choice)


@router.callback_query(cf.CategoryCall.filter(), AdminWorkSeat.AddSale.category_choice)
async def add_sale_category_choice(call: types.CallbackQuery,
                                   state: FSMContext,
                                   callback_data: cf.CategoryCall):
    category: models.Category = models.Category.objects.get(pk=callback_data.pk)
    await add_sale_category_choice_message(call.message, state, category)


async def add_sale_category_choice_message(message: types.Message,
                                           state: FSMContext,
                                           category: models.Category):
    data = await state.get_data()
    current_page = data.get('ProductPaginator_page', 1)
    text = 'Выберите нужный продукт: '
    key = inline.ProductPaginator(
        router,
        category,
        current_page=current_page,
        after_pagination_keyboards=[await inline.back('add_sale')]
    )
    try:
        await message.edit_text(text, reply_markup=key.as_markup())
    except Exception as e:
        logger.warning(e)
        await message.answer(text, reply_markup=key.as_markup())
    await state.update_data({'category': category})
    await state.set_state(AdminWorkSeat.AddSale.product_choice)


@router.callback_query(cf.ProductCall.filter(), AdminWorkSeat.AddSale.product_choice)
async def add_sale_product_choice_call(call: types.CallbackQuery,
                                       state: FSMContext,
                                       callback_data: cf.ProductCall):
    product: models.Product = models.Product.objects.get(pk=callback_data.pk)
    await add_sale_message(call.message, state, product)


async def add_sale_message(message: types.Message,
                           state: FSMContext,
                           product: models.Product):
    data = await state.get_data()
    sales_main: models.SalesMain = data.get('sales_main')
    team: models.Team = data.get('team')
    sales_main.add_sale(
        team=team,
        product=product
    )
    models.Product.check_and_update_products_status()
    await seat_choice_message(message, state, sales_main.seat)


#   COPY SALE
@router.callback_query(cf.CopySaleCall.filter(), AdminWorkSeat.SeatActions.main)
async def copy_sale_call(call: types.CallbackQuery,
                         state: FSMContext,
                         callback_data: cf.CopySaleCall):
    product: models.Product = models.Product.objects.get(pk=callback_data.pk)
    await add_sale_message(call.message, state, product)


#   JOIN SEAT
@router.callback_query(cf.AdminActionCall.filter(
    F.action == 'join_seat'
), AdminWorkSeat.SeatActions.main)
async def join_seat_call(call: types.CallbackQuery, state: FSMContext):
    data = await state.get_data()
    seat: models.Seat = data.get('seat')
    text = 'Выберите стол, с которым будем объединять: '
    key = await inline.concatenate_key(
        await inline.free_seat(),
        await inline.joined_seat(seat),
        await inline.back('seat_info')
    )
    try:
        await call.message.edit_text(text, reply_markup=key)
    except Exception as e:
        logger.warning(e)
        await call.message.answer(text, reply_markup=key)
    await state.set_state(AdminWorkSeat.SeatActions.join)


@router.callback_query(cf.SeatCall.filter(), AdminWorkSeat.SeatActions.join)
async def select_seat_to_join(call: types.CallbackQuery,
                              state: FSMContext,
                              callback_data: cf.SeatCall):
    join_seat: models.Seat = models.Seat.objects.filter(pk=callback_data.pk).first()
    data = await state.get_data()
    seat: models.Seat = data.get('seat')
    if not join_seat.join:
        join_seat.join_with(seat)
    else:
        join_seat.un_join_seat()
    await seat_choice_message(call.message, state, seat)


#   MOVE SEAT
@router.callback_query(cf.AdminActionCall.filter(
    F.action == 'move_seat'
), AdminWorkSeat.SeatActions.main)
async def move_seat_call(call: types.CallbackQuery, state: FSMContext):
    text = 'Выберите, куда пересадить: '
    key = await inline.concatenate_key(
        await inline.free_seat(),
        await inline.back('seat_info')
    )
    try:
        await call.message.edit_text(text, reply_markup=key)
    except Exception as e:
        logger.warning(e)
        await call.message.answer(text, reply_markup=key)
    await state.set_state(AdminWorkSeat.SeatActions.move)


@router.callback_query(cf.SeatCall.filter(), AdminWorkSeat.SeatActions.move)
async def select_new_seat_to_move(call: types.CallbackQuery,
                                  state: FSMContext,
                                  callback_data: cf.SeatCall):
    data = await state.get_data()
    sales_main: models.SalesMain = data.get('sales_main')
    new_seat: models.Seat = models.Seat.objects.filter(pk=callback_data.pk).first()
    sales_main.move_seat(new_seat)
    await state.update_data({'seat': new_seat})
    await seat_choice_message(call.message, state, new_seat)


@router.callback_query(cf.ProductCall.filter(), AdminWorkSeat.SeatActions.main)
async def seat_choice_product_call(call: types.CallbackQuery,
                                   state: FSMContext,
                                   callback_data: cf.ProductCall):
    product: models.Product = models.Product.objects.get(pk=callback_data.pk)
    await sales_product_choice_message(call.message, state, product)


async def sales_product_choice_message(message: types.Message,
                                       state: FSMContext,
                                       product: models.Product | None = None):
    data = await state.get_data()
    sales_main: models.SalesMain = data.get('sales_main')
    sales_id = data.get('sales_id', [])
    if product:
        if sales_main.sales.filter(product=product).count() == 1:
            sale = sales_main.sales.filter(product=product)
            return await sales_info_message(message, state, sale)
        else:
            await state.update_data({'product': product})
            current_page = data.get('SaleProductsPaginator_page', 1)
            key = inline.SaleProductsPaginator(
                router,
                sales_main=sales_main,
                product=product,
                current_page=current_page,
                after_pagination_keyboards=[await inline.back('seat_choice')]
            )
    else:
        confirm = await inline.confirm() if len(sales_id) > 0 else None
        current_page = data.get('SaleProductsPaginator_page', 1)
        key = inline.SaleProductsPaginator(
            router,
            sales_main=sales_main,
            current_page=current_page,
            checkout_mode=True,
            sales_id=sales_id,
            after_pagination_keyboards=[
                await inline.choice_all(),
                confirm,
                await inline.back('seat_choice')]
        )
    try:
        await message.edit_text('Выберите нужный товар', reply_markup=key.as_markup())
    except Exception as e:
        logger.warning(e)
        await message.answer('Выберите нужный товар', reply_markup=key.as_markup())
    await state.set_state(AdminWorkSeat.SeatActions.sales_choice)


@router.callback_query(cf.AdminActionCall.filter(
    F.action == 'sales_choice'
), AdminWorkSeat.SeatActions.main)
async def sales_choice_call(call: types.CallbackQuery,
                            state: FSMContext):
    await sales_product_choice_message(call.message, state, None)


@router.callback_query(cf.AdminActionCall.filter(
    F.action == 'choose_all'
), AdminWorkSeat.SeatActions.sales_choice)
async def choose_all_call(call: types.CallbackQuery,
                          state: FSMContext):
    data = await state.get_data()
    sales_id = data.get('sales_id', [])
    sales_main: models.SalesMain = data.get('sales_main')
    if len(sales_id) == sales_main.sales.count():
        sales_id = []
    else:
        sales_id = [sale for sale in sales_main.sales.values_list('id', flat=True)]
    await state.update_data({'sales_id': sales_id})
    await sales_product_choice_message(call.message, state, None)


@router.callback_query(cf.SaleCall.filter(), AdminWorkSeat.SeatActions.sales_choice)
async def sale_choice_call(call: types.CallbackQuery,
                           state: FSMContext,
                           callback_data: cf.SaleCall):
    data = await state.get_data()
    product: models.Product = data.get('product')
    sales_query: models.SalesQuerySet[models.Sales] = models.Sales.objects.filter(pk=callback_data.pk)
    if product:
        await sales_info_message(call.message, state, sales_query)
    else:
        sales_id = data.get('sales_id', [])
        if callback_data.pk in sales_id:
            sales_id.remove(callback_data.pk)
        else:
            sales_id.append(callback_data.pk)
        await state.update_data({'sales_id': sales_id})
        await sales_product_choice_message(call.message, state, product)


async def sales_info_message(message: types.Message,
                             state: FSMContext,
                             sales: models.SalesQuerySet[models.Sales]):
    text = sales.get_text()
    key = await inline.sales_actions()
    try:
        await message.edit_text(text, reply_markup=key)
    except Exception as e:
        logger.warning(e)
        await message.answer(text, reply_markup=key)
    await state.set_state(AdminWorkSeat.SalesActions.main)
    await state.update_data({'sales': sales})


@router.callback_query(cf.ActionCall.filter(
    F.action == 'confirm'
), AdminWorkSeat.SeatActions.sales_choice)
async def confirm_sales_choice_call(call: types.CallbackQuery,
                                    state: FSMContext):
    data = await state.get_data()
    sales_id: list[int] = data.get('sales_id', [])
    sales = models.Sales.objects.filter(pk__in=sales_id)
    await sales_info_message(call.message, state, sales)


@router.message(F.text.startswith('USER'), F.via_bot, AdminWorkSeat.SalesActions.main)
async def set_user_for_sale(message: types.Message,
                            state: FSMContext):
    message_data = message.text.replace('USER ', '').split(' ')
    user_pk: int = int(message_data[0])
    user_discount: float = float(message_data[-1])
    user: models.TgUser = models.TgUser.objects.filter(pk=user_pk).first()
    old_balance_value = user.balance
    data = await state.get_data()
    sales: models.SalesQuerySet[models.Sales] = data.get('sales')
    sales.update(user=user)
    rest_discount_sum = sales.set_discount(user_discount)
    user.balance -= user_discount - rest_discount_sum
    new_balance_value = user.balance
    if old_balance_value != new_balance_value:  
        user.save('Списание бонусов.')
    await sales_info_message(message, state, sales)


@router.callback_query(cf.ActionCall.filter(
    F.action == 'cancel'
), AdminWorkSeat.SalesActions())
async def sales_actions_cancel_call(call: types.CallbackQuery,
                                    state: FSMContext):
    await call.answer('Отменил действие!')
    await call.message.delete()
    await state.set_state(AdminWorkSeat.SalesActions.main)


@router.callback_query(cf.AdminActionCall.filter(
    F.action == 'set_discount'
), AdminWorkSeat.SalesActions.main)
async def set_discount_call(call: types.CallbackQuery,
                            state: FSMContext):
    text = 'Укажите скидку: '
    key = await inline.cancel()
    await call.message.answer(text, reply_markup=key)
    await state.set_state(AdminWorkSeat.SalesActions.set_discount)


@router.message(AdminWorkSeat.SalesActions.set_discount)
async def sales_set_discount_message(message: types.Message,
                                     state: FSMContext):
    try:
        discount_sum = float(message.text)
    except Exception as e:
        logger.warning(e)
        return await not_correct_input(message)
    data = await state.get_data()
    sales: models.SalesQuerySet = data.get('sales')
    sales.set_discount(discount_sum)
    await sales_info_message(message, state, sales)


@router.callback_query(cf.AdminActionCall.filter(
    F.action.in_({'cash', 'sber', 'terminal'})
), AdminWorkSeat.SalesActions.main)
async def sales_pay_type_choice_call(call: types.CallbackQuery,
                                     state: FSMContext,
                                     callback_data: cf.AdminActionCall):
    data = await state.get_data()
    match callback_data.action:
        case 'cash':
            pay_type = 'Наличка'
        case 'sber':
            pay_type = 'СберБанк'
        case 'terminal':
            pay_type = 'Терминал'
        case _:
            raise ValueError
    await state.update_data({'pay_type': callback_data.action})
    sales: models.SalesQuerySet = data.get('sales')
    text = f'Введите сумму для оплаты {pay_type}: '
    rest_sum = sales.get_rest_sum()
    key = await inline.concatenate_key(
        await inline.pay_sum_keyboard(rest_sum),
        await inline.cancel()
    )
    await call.message.answer(text, reply_markup=key)
    await state.set_state(AdminWorkSeat.SalesActions.pay)


@router.callback_query(cf.PayCall.filter(), AdminWorkSeat.SalesActions.pay)
async def sales_pay_call(call: types.CallbackQuery,
                         state: FSMContext,
                         callback_data: cf.PayCall):
    data = await state.get_data()
    sales: models.SalesQuerySet = data.get('sales')
    pay_type: str = data.get('pay_type')
    sales.pay(callback_data.amount, pay_type)
    await sales_info_message(call.message, state, sales)


@router.message(AdminWorkSeat.SalesActions.pay)
async def sales_pay_message(message: types.Message,
                            state: FSMContext):
    try:
        sum_amount = float(message.text)
    except Exception as e:
        logger.warning(e)
        return await not_correct_input(message)
    data = await state.get_data()
    sales: models.SalesQuerySet = data.get('sales')
    pay_type: str = data.get('pay_type')
    user_rest_sum = sales.pay(sum_amount, pay_type)
    if user_rest_sum > 0:
        msg = await message.answer(f'Отдайте {user_rest_sum}руб. сдачи!')
        await asyncio.sleep(2)
        await msg.delete()
    await sales_info_message(message, state, sales)


@router.callback_query(cf.ActionCall.filter(
    F.action == 'delete'
), AdminWorkSeat.SalesActions.main)
async def sales_delete_call(call: types.CallbackQuery,
                            state: FSMContext):
    data = await state.get_data()
    seat = data.get('seat')
    sales: models.SalesQuerySet = data.get('sales')
    sales.delete()
    models.Product.check_and_update_products_status()
    await seat_choice_message(call.message, state, seat)

